package com.scs.application.config;

import com.scs.application.core.exception.BusinessException;
import com.scs.application.core.exception.ValidException;
import com.scs.application.core.utils.BeanValidators;
import com.scs.application.core.utils.UserUtils;
import com.scs.application.core.utils.UtilCommon;
import com.scs.application.core.utils.UtilJavax;
import com.scs.application.interceptor.RequestWrapper;
import com.scs.application.modules.sys.entity.Log;
import com.scs.application.modules.sys.enums.BusType;
import com.scs.application.modules.sys.enums.LogType;
import com.scs.application.modules.sys.model.RestResult;
import com.scs.application.modules.sys.service.LogService;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.ExpiredCredentialsException;
import org.apache.shiro.authz.AuthorizationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import java.util.Collections;
import java.util.List;

/**
 * @Description：异常处理配置
 */
@Slf4j
@RestControllerAdvice
public class ExceptionHandleConfig {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    @Lazy
    protected LogService logService;

    //保存日志
    private void saveLog(Exception ex,ServletRequest servletRequest) {
        long tms=System.currentTimeMillis();
        logger.error("ExceptionHandleConfig.commonExceptionHandle."+tms+"\n",ex);
        String errMsg = UtilJavax.getStackTrace(ex);
        Log log=new Log();
        log.setLogType(LogType.error).setBusType(BusType.type00);
        String servletPath="无",body="无";

        String userCode="未知",userName="未知",opIp="";
        if (UserUtils.currentUser() != null) {
            userCode=UserUtils.currentUser().getCode();
            userName=UserUtils.currentUser().getName();
        }
        if(servletRequest instanceof HttpServletRequest) {
            servletPath = ((HttpServletRequest) servletRequest).getServletPath();
            RequestWrapper requestWrappercustom = new RequestWrapper((HttpServletRequest) servletRequest);
            body = requestWrappercustom.getBody();
            opIp= UtilJavax.getIPAddress(((HttpServletRequest) servletRequest));
        }

        log.setMsg("请求路径："+servletPath+",日志搜索标识: "+tms+"\n异常信息："+ UtilCommon.getStringPre(errMsg,5000)).setParam("请求参数："+body)
                .setIp(opIp).setCreatorName(userName).setCreator(userCode);
        logService.save(log);
    }

    @ExceptionHandler(value = {Exception.class,Error.class})
    public ResponseEntity<RestResult> commonExceptionHandle(Exception ex, ServletRequest servletRequest) {
        this.saveLog(ex,servletRequest);
        if (ex.getCause() != null && ex.getCause() instanceof DuplicateKeyException) { //这种错误，一般是唯一索引或者主键索引重复，排除程序bug，一般都是并发读写问题
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(
                    RestResult.error(HttpStatus.INTERNAL_SERVER_ERROR, "系统繁忙，请稍候重试！", null));
        }
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(
                RestResult.error(HttpStatus.INTERNAL_SERVER_ERROR, "服务器异常，原因:"+(ex!=null?ex.getMessage():"未知"), null));
    }

    @ExceptionHandler(value = {BusinessException.class})
    public ResponseEntity<RestResult> businessExceptionHandle(BusinessException ex, ServletRequest servletRequest) {
        this.saveLog(ex,servletRequest);
        if (ex.getCause() != null && ex.getCause() instanceof DuplicateKeyException) {
            return duplicateKeyExceptionExceptionnHandle((DuplicateKeyException) ex.getCause());
        }
//        ex.printStackTrace();
        return ResponseEntity.status(HttpStatus.OK).header("businessException","1").body(RestResult.error(ex.getMessage(), null));
    }

    @ExceptionHandler(value = {ValidException.class})
    public ResponseEntity<RestResult> validExceptionHandle(BusinessException ex, ServletRequest servletRequest) {
        return ResponseEntity.status(HttpStatus.OK).header("validException","1").body(RestResult.error(ex.getMessage(), null));
    }


    @ExceptionHandler(value = {ConstraintViolationException.class})
    public ResponseEntity<RestResult> validationExceptionHandle(ConstraintViolationException ex) {
        return ResponseEntity.status(HttpStatus.OK).body(
                RestResult.error(StringUtils.join(BeanValidators.extractMessage(ex)), null)
        );
    }

    @ExceptionHandler(value = {BindException.class})
    public ResponseEntity<RestResult> bindExceptionHandle(BindException ex) {
        List<ObjectError> allErrors = ex.getBindingResult().getAllErrors();

        List<String> errorMessages = Lists.newArrayList();
        for (ObjectError error : allErrors) {
            errorMessages.add(error.getDefaultMessage());
        }
        return ResponseEntity.status(HttpStatus.OK).body(RestResult.error(StringUtils.join(errorMessages, ","), null));
    }

    @ExceptionHandler(value = {MethodArgumentNotValidException.class})
    public ResponseEntity<RestResult> validationExceptionHandle(MethodArgumentNotValidException ex) {
        List<ObjectError> allErrors = ex.getBindingResult().getAllErrors();

        List<String> errorMessages = Lists.newArrayList();
        for (ObjectError error : allErrors) {
            errorMessages.add(error.getDefaultMessage());
        }
        return ResponseEntity.status(HttpStatus.OK).body(RestResult.error(StringUtils.join(errorMessages), null));
    }


    @ExceptionHandler(value = {DuplicateKeyException.class})
    public ResponseEntity<RestResult> duplicateKeyExceptionExceptionnHandle(DuplicateKeyException ex) {
        return ResponseEntity.status(HttpStatus.OK).body(RestResult.error("重复的主键值！", null));
    }

    @ExceptionHandler(value = {AuthenticationException.class})
    public ResponseEntity<RestResult> authenticationExceptionHandle(AuthenticationException ex) {
        ex.printStackTrace();
        if (ex instanceof ExpiredCredentialsException) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(RestResult.error(HttpStatus.UNAUTHORIZED, "token已过期，请重新登录", Collections.singletonMap("needLogin", true)));
        }
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(RestResult.error(HttpStatus.UNAUTHORIZED, "您还未登录，请先登录", null));
    }

    @ExceptionHandler(value = {AuthorizationException.class})
    public ResponseEntity<RestResult> authorizationExceptionHandle(AuthorizationException ex) {
        ex.printStackTrace();
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body(RestResult.error(HttpStatus.FORBIDDEN, "您没有此资源的操作权限", null));
    }

}
